package mysql

import (
	"context"
	"github.com/go-xorm/xorm"
	"github.com/pkg/errors"
	"go.uber.org/zap"
)

// SqlInfo SQL 操作信息
type SqlInfo struct {
	MessageId string          // 请求唯一 ID
	Ctx       context.Context // 请求上下文
	// 基础信息
	Table string // 操作表名称，存在 Order、Group、Join 条件时必传
	Alias string // 操作表别名
	// 必传，操作数据表模型对象
	// 1. 操作单条数据的数据模型 *model
	// 2. 插入多条数据的数据模型 []*model
	// 3. 查询多条数据的数据模型容器, []*model
	Obj interface{} // 【必】存储数据模型的指针地址
	// 操作字段
	Field []string // 查询指定字段，如："a.*, (select name from b limit 1) as name","`id`,`nickname`,`account`"
	Cols  []string // 更新操作指定字段，如：[]string{"status", "update_time"}
	Omit  []string // 排除操作指定字段，如：[]string{"id", "uuid"}
	// 查询条件
	AutoCondition bool                // 是否要禁用 xorm 根据模型数据值自动生成条件，false-不禁用（默认）true-禁用
	Id            int                 // 修改、删除操作时 Id 条件
	Where         []*Condition        // 查询条件
	And           *ConnCondition      // AND 连接条件
	Or            *ConnCondition      // 或连接条件
	AndChild      *ConnChildCondition // AND 子查询信息
	OrChild       *ConnChildCondition // OR 子查询信息
	// 连表查询条件
	Join []*JoinInfo // 关联查询信息，如：[]*JoinInfo{{Type: "LEFT", Table: "user", Alias:"u", On: "`o`.`uid` = `u`.`id`"}}
	// 分页查询条件
	Start int // 分页查询，开始位置
	Page  int // 开始页数
	Rows  int // 分页查询，每页显示数量
	// 分组查询条件
	Group  string // 分组字段
	Having string // 分组查询条件
	// 排序条件
	Order string // 排序条件，如："`time` DESC"
	// 事务操作字段
	Func string // 事务操作时，需要指明操作方法，值范围：[Insert-插入数据\Update-修改数据\Delete-删除数据]
	// Aurora 数据库写入转发模式
	// 参考：https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-global-database-write-forwarding.html#aurora-global-database-write-forwarding-compatibility
	AuroraReplicaReadConsistency string // 仅在服务运行模式为 test/release 的模式下生效，值范围：eventual、session、global
}

type Mysql interface {
	// Exist 判断数据是否存在
	Exist(info *SqlInfo) (has bool, err error)
	// Get 获取单条数据
	Get(info *SqlInfo) (has bool, err error)
	// Count 统计查询结果数量
	Count(info *SqlInfo) (total int64, err error)
	// Find 获取数据列表
	Find(info *SqlInfo) (err error)
	// FindAndCount 查询并统计结果数据量
	FindAndCount(info *SqlInfo) (total int64, err error)
	// Sum 计算某个字段的和，返回 float64 类型值
	Sum(info *SqlInfo) (sum float64, err error)
	// SumInt 计算某个字段的和，返回 int64
	SumInt(info *SqlInfo) (sum int64, err error)
	// Sums 计算某几个个字段的和，返回 []float64
	Sums(info *SqlInfo) (sum []float64, err error)
	// SumsInt 计算某几个字段的和，返回 []int64
	SumsInt(info *SqlInfo) (sum []int64, err error)
	// InsertOne 单条 插入数据，返回受影响行数
	InsertOne(info *SqlInfo) (affected int64, err error)
	// InsertList 事务执行批量插入数据，返回受影响行数
	InsertList(info *SqlInfo) (affected int64, err error)
	// Update 修改数据
	Update(info *SqlInfo) (affected int64, err error)
	// Delete 删除数据
	Delete(info *SqlInfo) (affected int64, err error)
	// QueryString 查询列表，返回 map[string]string 结构的切片结果
	QueryString(info *SqlInfo) (result []map[string]string, err error)
	// QuerySliceString 查询列表，返回 []string 结构的切片结果
	QuerySliceString(info *SqlInfo) (result [][]string, err error)
	// QueryInterface 查询列表，返回 map[string]interface{} 结构的切片结果，可以通过这个结构转结构体
	QueryInterface(info *SqlInfo) (result []map[string]interface{}, err error)
	// QueryBytes 查询列表，返回 map[string][]byte 结构的切片结果
	QueryBytes(info *SqlInfo) (result []map[string][]byte, err error)
	// TransactionWork 事务操作
	TransactionWork(infos []*SqlInfo) (values []int64, err error)
}

// Handler 操作助手
type Handler struct {
	Engine      *xorm.Engine      // 数据库操作引擎
	EngineGroup *xorm.EngineGroup // 主从数据库操作引擎
	Logger      *zap.Logger       // 日志客户端
	Mode        string            // 服务运行模式，debug/test/release
}

// ConnCondition 普通查询组合条件
type ConnCondition struct {
	ConnType  string       // 条件组关联方式，AND、OR
	Condition []*Condition // 构建组合条件
}

// ConnChildCondition 子查询组合条件
type ConnChildCondition struct {
	ConnType  string            // 条件组关联方式，AND、OR
	Condition []*ChildCondition // 子查询组合条件
}

// JoinInfo 连表条件
type JoinInfo struct {
	Type  string        `json:"type"`  // 关联方式
	Table string        `json:"table"` // 关联表名称
	Alias string        `json:"alias"` // 关联表别名
	On    string        `json:"on"`    // 关联条件字符串
	Args  []interface{} `json:"args"`  // 关联条件值
}

// Exist 判断数据是否存在
func (h *Handler) Exist(info *SqlInfo) (has bool, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return has, errors.WithStack(err)
	}
	defer session.Close()

	has, err = session.Exist(info.Obj)
	return has, errors.WithStack(err)
}

// Get 获取单条数据
func (h *Handler) Get(info *SqlInfo) (has bool, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return has, errors.WithStack(err)
	}
	defer session.Close()

	has, err = session.Get(info.Obj)
	return has, errors.WithStack(err)
}

// Count 统计查询结果数量
func (h *Handler) Count(info *SqlInfo) (total int64, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	defer session.Close()

	total, err = session.Count(info.Obj)
	return total, errors.WithStack(err)
}

// Find 获取数据列表
func (h *Handler) Find(info *SqlInfo) (err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return errors.WithStack(err)
	}
	defer session.Close()

	// Obj 应该为 []*model 的指针变量
	return errors.WithStack(session.Find(info.Obj))
}

// FindAndCount 查询并统计结果数据量
func (h *Handler) FindAndCount(info *SqlInfo) (total int64, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	defer session.Close()

	// Obj 应该为 []*model 的指针变量
	total, err = session.FindAndCount(info.Obj)
	return total, errors.WithStack(err)
}

// Sum 计算某个字段的和，返回 float64 类型值
func (h *Handler) Sum(info *SqlInfo) (sum float64, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	defer session.Close()

	if len(info.Cols) != 1 {
		err = errors.New("sum()：sql cols is invalid")
		return
	}

	sum, err = session.Sum(info.Obj, info.Cols[0])
	return sum, errors.WithStack(err)
}

// SumInt 计算某个字段的和，返回 int64
func (h *Handler) SumInt(info *SqlInfo) (sum int64, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	defer session.Close()

	if len(info.Cols) != 1 {
		err = errors.New("sumInt()：sql cols is invalid")
		return
	}

	sum, err = session.SumInt(info.Obj, info.Cols[0])
	return sum, errors.WithStack(err)
}

// Sums 计算某几个个字段的和，返回 []float64
func (h *Handler) Sums(info *SqlInfo) (sum []float64, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return sum, err
	}
	defer session.Close()

	if len(info.Cols) == 0 {
		err = errors.New("sums()：sql cols is invalid")
		return
	}

	sum, err = session.Sums(info.Obj, info.Cols...)
	return sum, errors.WithStack(err)
}

// SumsInt 计算某几个字段的和，返回 []int64
func (h *Handler) SumsInt(info *SqlInfo) (sum []int64, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return sum, err
	}
	defer session.Close()

	if len(info.Cols) == 0 {
		err = errors.New("sumsInt()：sql cols is invalid")
		return
	}

	sum, err = session.SumsInt(info.Obj, info.Cols...)
	return sum, errors.WithStack(err)
}

// InsertOne 单条 插入数据，返回受影响行数
func (h *Handler) InsertOne(info *SqlInfo) (affected int64, err error) {
	//session, err := h.pretreatment(info)
	//if err != nil {
	//	return 0, errors.WithStack(err)
	//}
	//defer session.Close()
	//
	//session, err = h.setAuroraReplicaReadConsistency(session, info.AuroraReplicaReadConsistency)
	//if err != nil {
	//	return
	//}
	//
	//affected, err = session.InsertOne(info.Obj)
	//if err != nil {
	//	logger.Error("InsertOne error", zap.Error(err), zap.Any("info", info))
	//}
	//return

	// 事务操作插入数据
	info.Func = "Insert"
	value, err := h.transactionWork([]*SqlInfo{info}, info.AuroraReplicaReadConsistency)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	return value[0], nil
}

// InsertList 事务执行批量插入数据，返回受影响行数
func (h *Handler) InsertList(info *SqlInfo) (affected int64, err error) {
	//session, err := h.pretreatment(info)
	//if err != nil {
	//	return 0, errors.WithStack(err)
	//}
	//defer session.Close()
	//
	//session, err = h.setAuroraReplicaReadConsistency(session, info.AuroraReplicaReadConsistency)
	//if err != nil {
	//	return
	//}
	//
	//affected, err = session.Insert(info.Obj)
	//if err != nil {
	//	logger.Error("Insert error", zap.Error(err), zap.Any("info", info))
	//}
	//return

	// 事务操作批量插入（避免出现某条数据插入失败导致业务问题）
	info.Func = "Insert"
	value, err := h.transactionWork([]*SqlInfo{info}, info.AuroraReplicaReadConsistency)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	return value[0], nil
}

// Update 修改数据
func (h *Handler) Update(info *SqlInfo) (affected int64, err error) {
	// 判断是否存在更新条件
	if info.Id == 0 && len(info.Where) == 0 && info.And == nil && info.Or == nil {
		err = errors.New("update：sql condition cannot be empty")
		return
	}
	//
	//session, err := h.pretreatment(info)
	//if err != nil {
	//	return 0, errors.WithStack(err)
	//}
	//defer session.Close()
	//
	//session, err = h.setAuroraReplicaReadConsistency(session, info.AuroraReplicaReadConsistency)
	//if err != nil {
	//	return
	//}
	//
	//affected, err = session.Update(info.Obj)
	//if err != nil {
	//	logger.Error("Update error", zap.Error(err), zap.Any("info", info))
	//}
	//return

	// 事务操作更新（避免出现大面积数据更新 SQL）
	info.Func = "Update"
	value, err := h.transactionWork([]*SqlInfo{info}, info.AuroraReplicaReadConsistency)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	return value[0], nil
}

// Delete 删除数据
func (h *Handler) Delete(info *SqlInfo) (affected int64, err error) {
	// 判断是否存在删除条件
	if info.Id == 0 && len(info.Where) == 0 && info.And == nil && info.Or == nil && info.AndChild == nil && info.OrChild == nil {
		err = errors.New("delete：sql condition cannot be empty")
		return
	}

	//session, err := h.pretreatment(info)
	//if err != nil {
	//	return 0, errors.WithStack(err)
	//}
	//defer session.Close()
	//
	//session, err = h.setAuroraReplicaReadConsistency(session, info.AuroraReplicaReadConsistency)
	//if err != nil {
	//	return
	//}
	//
	//affected, err = session.Delete(info.Obj)
	//if err != nil {
	//	logger.Error("Delete error", zap.Error(err), zap.Any("info", info))
	//}
	//return

	// 事务执行删除（避免出现大面积数据删除 SQL）
	info.Func = "Delete"
	value, err := h.transactionWork([]*SqlInfo{info}, info.AuroraReplicaReadConsistency)
	if err != nil {
		return 0, errors.WithStack(err)
	}
	return value[0], nil
}

// QueryString 查询列表，返回 map[string]string 结构的切片结果
func (h *Handler) QueryString(info *SqlInfo) (result []map[string]string, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return result, errors.WithStack(err)
	}
	defer session.Close()

	result, err = session.QueryString()
	return result, errors.WithStack(err)
}

// QuerySliceString 查询列表，返回 []string 结构的切片结果
func (h *Handler) QuerySliceString(info *SqlInfo) (result [][]string, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return result, errors.WithStack(err)
	}
	defer session.Close()

	result, err = session.QuerySliceString()
	return result, errors.WithStack(err)
}

// QueryInterface 查询列表，返回 map[string]interface{} 结构的切片结果，可以通过这个结构转结构体
func (h *Handler) QueryInterface(info *SqlInfo) (result []map[string]interface{}, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return result, errors.WithStack(err)
	}
	defer session.Close()

	result, err = session.QueryInterface()
	return result, errors.WithStack(err)
}

// QueryBytes 查询列表，返回 map[string][]byte 结构的切片结果
func (h *Handler) QueryBytes(info *SqlInfo) (result []map[string][]byte, err error) {
	session, err := h.pretreatment(info)
	if err != nil {
		return result, errors.WithStack(err)
	}
	defer session.Close()

	result, err = session.Query()
	return result, errors.WithStack(err)
}

// TransactionWork 事务操作，Aurora 数据库写入转发模式采用第一条 Sql 的设置
func (h *Handler) TransactionWork(infos []*SqlInfo) (values []int64, err error) {
	if len(infos) == 0 || infos[0] == nil {
		return nil, errors.New("transaction work param is abnormal")
	}
	values, err = h.transactionWork(infos, infos[0].AuroraReplicaReadConsistency)
	return values, errors.WithStack(err)
}
